#include "queue.h"

using namespace std;

Queue::Queue(int nmax_core, int nmin_core){
  min_core=nmin_core;
  max_core=nmax_core;
  mod_core=max_core-min_core+1;
  priority=0; /* Sets the current core priority state */

  /* Error Handling */
  if(max_core<min_core){
    printf("ERROR: QUEUE::max core < min core");
    exit(1);
  }
}

bool Queue::Empty() {
  return oq.empty() && aq.empty() && iq.empty() && dq.empty();
}

void Queue::NewReq(int nsrc, int naddr, int ndelay, int L2src, Cmd ncmd){
  /* Copy new data into an element */
  QueueElement newQE;
  newQE.src=nsrc;
  newQE.addr=naddr;
  newQE.delay=ndelay;
  newQE.cmd=ncmd;
  newQE.L2src = L2src;

  /* Push the new element into the queue */
  iq.push_back(newQE);
}

void Queue::NewReq(QueueElement new_cmd) {
  NewReq(new_cmd.src, new_cmd.addr, new_cmd.delay, new_cmd.L2src, new_cmd.cmd);
}

void Queue::Commit_Reqs(int n){
  int new_priority=-2;
  for(int k=0; k<n; k++){
    int i=GetNextNewReq(); /* Get the next request ready to be committed */
    if(i==-1) break; /* There are no more requests left */

    new_priority=iq[i].src+1-min_core; /* Speculate new priority */
    dq.push_back(iq[i]); /* Add it to the decrementing list */
    iq.erase(iq.begin()+i); /* Removing the index from this list commits it */
  }
  if(new_priority==-2) return; /* No requests committed, dont change priority */
  if(new_priority>=mod_core) new_priority=0; /* Speculated priority needs to roll over */
  priority=new_priority; /* Update priority */
}

/* At the beginning of a clock cycle, this will decrement all timers */
void Queue::Countdown(){
  /* We have to decrement backwards to ensure that the delete() function
   * doesn't mess up the index orders while we increment. */
  for(int i=dq.size()-1; i>=0; i--){
    dq[i].delay--; /* Decrement everything */
    /* If the delay is finished, store that value for later */
    if(dq[i].delay==0){
      oq.push_back(dq[i]);
      dq.erase(dq.begin()+i);
    }
  }
}

int Queue::GetMinCore(){return min_core;}
int Queue::GetMaxCore(){return max_core;}
int Queue::GetModCore(){return mod_core;}
int Queue::GetPriority(){return priority;}

/* Private Functions */

/* Used to implement round robin selections in the IQ */
int Queue::GetNextNewReq(){
  int iqsize=iq.size(); /* All requests in this vector have 0 delay left */
  int best_priority=mod_core+1; /* This is one more than the worst priority match */
  int best_priorityi=-1; /* The priority match's index */
  int new_priority;
  for(int i=0; i<iqsize; i++){
    new_priority=PD(iq[i].src);
    if(new_priority<best_priority){ /* Did we find a closer priority match? */
      best_priority=new_priority;
      best_priorityi=i;
      if(best_priority==0) break; /* We found a perfect priority match, no need to move on */
    }
  }
  return best_priorityi; /* No more requests if -1 */
}

int Queue::PD(int p){
  int modp=p-min_core;
  if(modp>priority) modp-=mod_core;
  return priority-modp;
}
